/***************************************************************************
 *   Copyleft 2006-2007 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#ifndef __MEMORYTRACKER_H__
#define __MEMORYTRACKER_H__

#ifndef NDEBUG

#include <iostream>
#include <ostream>
#include <fstream>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <new>
#include "../Types/Types.h"

#define MEMORY_TRACKER_FILE				"memory_leaks.txt"

typedef std::ofstream Output;

class MemoryTracker {
private:
	struct MemoryNode {
		cVoid* mAllocated;
		cUInt mMemSize;
		cInt mLineNr;
		cChar mBuffer[MAX_PATH];
		MemoryNode* mNextNode;
	} *mHead;
	Output mOutput;
	cBool mPrintedLeaks;
public:

    /**************************************************************
    * Constructors                                                *
    ***************************************************************/
	MemoryTracker(): mHead(cNull), mPrintedLeaks(false) { }
	~MemoryTracker() {
		if (!mPrintedLeaks) {
			PrintLeaks();
		}
	}

    cVoid PrintLeaks() {
		mPrintedLeaks = true;
		mOutput.open(MEMORY_TRACKER_FILE);

		if (mHead) {
			MemoryTracker::MemoryNode* it;
			cChar buffer[MAX_PATH + 400];
			mOutput<<"*********** Memory leaks !!! ************"<<std::endl;

			for (it = mHead; it != cNull; it = it->mNextNode) {
				sprintf_s(buffer, "[%s] line: %d, size: %d\n", it->mBuffer, it->mLineNr, it->mMemSize);
				mOutput<<buffer<<std::endl;
			}
			mOutput<<"*****************************************"<<std::endl;
		} else {
			mOutput<<"------------ No memory leaks ------------"<<std::endl;
		}
		mOutput.close();
	}
        
    /**************************************************************
    * Operators                                                   *
    ***************************************************************/


    /**************************************************************
    * Other helpfull functions                                    *
    ***************************************************************/
	cVoid AddAllocation(const cChar *file, cInt line, cVoid *pointer, cUInt size) {
		MemoryTracker::MemoryNode* node = (MemoryTracker::MemoryNode*) malloc(sizeof(MemoryTracker::MemoryNode));
		strcpy_s(node->mBuffer, file);
		node->mLineNr = line;
		node->mAllocated = pointer;
		node->mMemSize = size;
		node->mNextNode = mHead;
		mHead = node;
	}

	cVoid DelAllocation(cVoid *pointer) {
		MemoryTracker::MemoryNode* previt = cNull;
		MemoryTracker::MemoryNode* it;

		for (it = mHead; it != cNull; previt = it, it = it->mNextNode) {
			if (it->mAllocated == pointer)	{
				if (previt) {
					previt->mNextNode = it->mNextNode;
				} else {
					mHead = it->mNextNode;
				}
				free(it);
				break;
			}
		}
	}
};                                                                 

extern MemoryTracker MEMORY_TRACKING_SYSTEM;

inline void* operator new(cUInt size, const cChar *file, const cInt line) {
	void *p = malloc(size);
	if (p) {
		MEMORY_TRACKING_SYSTEM.AddAllocation(file, line, p, size);
	}
	return p;
}

inline void* operator new[](cUInt size, const cChar *file, const cInt line) {
	void *p = malloc(size);
	if (p) {
		MEMORY_TRACKING_SYSTEM.AddAllocation(file, line, p, size);
	}
	return p;
}

inline void operator delete(cVoid *p) throw() {
	MEMORY_TRACKING_SYSTEM.DelAllocation(p);
	free(p);
}

inline void operator delete(cVoid *p, const cChar *file, const cInt line) {
	MEMORY_TRACKING_SYSTEM.DelAllocation(p);
	free(p);
}

inline void operator delete[](cVoid *p, const cChar *file, const cInt line) {
	MEMORY_TRACKING_SYSTEM.DelAllocation(p);
	free(p);
}

#define new new(__FILE__, __LINE__)
#define START_MEMORY_TRACKING_SYSTEM MemoryTracker MEMORY_TRACKING_SYSTEM;
#define STOP_MEMORY_TRACKING_SYSTEM MEMORY_TRACKING_SYSTEM.PrintLeaks();

#else   // #ifndef NDEBUG

#define START_MEMORY_TRACKING_SYSTEM

#endif  // #ifndef NDEBUG

#endif // __MEMORYTRACKER_H__